﻿using StructureMap;
using System.Collections;
using System.Threading;
using System.Web;

namespace System.Data.Entity.Extensions.UnitOfWork
{
    /// <summary>
    /// 
    /// </summary>
    public static class UnitOfWork
    {
        #region Factory

        /// <summary>
        /// 
        /// </summary>
        private static IUnitOfWorkFactory _factory;

        /// <summary>
        /// 
        /// </summary>
        private static IUnitOfWorkFactory Factory
        {
            get
            {
                return UnitOfWork._factory;
            }
            set
            {
                if (value != UnitOfWork._factory)
                {
                    UnitOfWork._factory = value;
                }
            }
        }

        #endregion

        #region Threads

        /// <summary>
        /// 
        /// </summary>
        private static Hashtable _threads;

        /// <summary>
        /// 
        /// </summary>
        private static Hashtable Threads
        {
            get
            {
                if (UnitOfWork._threads == null)
                {
                    UnitOfWork._threads = new Hashtable();
                }

                return UnitOfWork._threads;
            }
        }

        #endregion

        #region Current
        
        /// <summary>
        /// 
        /// </summary>
        private const string HttpContextKey = "System.Data.Entity.Extensions.UnitOfWork.Key";

        /// <summary>
        /// 
        /// </summary>
        public static IUnitOfWork Current
        {

            get
            {
                IUnitOfWork unitOfWork = UnitOfWork.Get();

                if (unitOfWork == null)
                {
                    UnitOfWork.Factory = ObjectFactory.GetInstance<IUnitOfWorkFactory>();
                    unitOfWork = UnitOfWork.Factory.Create();
                    UnitOfWork.Save(unitOfWork);
                }

                return unitOfWork;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private static IUnitOfWork Get()
        {
            if (HttpContext.Current == null)
            {
                if (string.IsNullOrEmpty(Thread.CurrentThread.Name))
                {
                    Thread.CurrentThread.Name = Guid.NewGuid().ToString();

                    return null;
                }
                else
                {
                    lock (UnitOfWork.Threads.SyncRoot)
                    {
                        return (IUnitOfWork)UnitOfWork.Threads[Thread.CurrentThread.Name];
                    }
                }
            }
            else
            {
                if (HttpContext.Current.Items.Contains(HttpContextKey))
                {
                    return (IUnitOfWork)HttpContext.Current.Items[HttpContextKey];
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="unitOfWork"></param>
        private static void Save(IUnitOfWork unitOfWork)
        {
            if (HttpContext.Current == null)
            {
                lock (UnitOfWork.Threads.SyncRoot)
                {
                    UnitOfWork.Threads[Thread.CurrentThread.Name] = unitOfWork;
                }
            }
            else
            {
                HttpContext.Current.Items[HttpContextKey] = unitOfWork;
            }
        }

        #endregion
        
        #region IUnitOfWork
        
        /// <summary>
        /// 
        /// </summary>
        public static void Commit()
        {
            IUnitOfWork unitOfWork = UnitOfWork.Get();

            if (unitOfWork != null)
            {
                unitOfWork.Commit();
            }
        }

        #endregion
    }
}
